package com.imflea.zero.service.wx.impl;

import cn.hutool.core.bean.BeanUtil;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.imflea.zero.constant.PayConstant;
import com.imflea.zero.constant.WxPayConstant;
import com.imflea.zero.exception.BizException;
import com.imflea.zero.model.entity.pay.PayRecorderInfo;
import com.imflea.zero.model.entity.pay.dto.PayOrderDetailDto;
import com.imflea.zero.model.entity.pay.dto.PayPreRecorderDto;
import com.imflea.zero.model.entity.pay.dto.PayRefundOrderDto;
import com.imflea.zero.model.entity.pay.dto.PaySuccessDto;
import com.imflea.zero.model.entity.pay.vo.PayPreDataVo;
import com.imflea.zero.model.entity.pay.vo.PayPreRefundVo;
import com.imflea.zero.model.entity.qx.QxTenantConfig;
import com.imflea.zero.model.entity.wx.config.FarmWXPayConfig;
import com.imflea.zero.model.entity.wx.config.WxPayBaseConfig;
import com.imflea.zero.service.meeting.IMeetingOrderInfoService;
import com.imflea.zero.service.pay.IHandleCommonPrePayService;
import com.imflea.zero.service.pay.IPayRecorderInfoService;
import com.imflea.zero.service.pay.impl.HandleMeetingPrePayServiceImpl;
import com.imflea.zero.service.qx.IQxTenantConfigService;
import com.imflea.zero.service.wx.IWxPayService;
import com.imflea.zero.util.ZeroJsonUtils;
import com.imflea.zero.util.base.CommonUtils;
import com.imflea.zero.utils.OrderUtils;
import com.imflea.zero.utils.SessionUtils;
import com.imflea.zero.utils.WxPayUtils;
import com.imflea.zero.utils.ZeroApplicationContextUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author 祥保玉
 * @PackageName: com.imflea.zero.service.wx.impl
 * @ClassName: WxPayServiceImpl
 * @Description
 * @date 2021-09-26  13:27:05
 */

@Service
public class WxPayServiceImpl implements IWxPayService {

    private final Logger logger = LoggerFactory.getLogger(WxPayServiceImpl.class);

    @Autowired
    private IMeetingOrderInfoService meetingOrderInfoService;
    @Autowired
    private IPayRecorderInfoService payRecorderInfoService;
    @Autowired
    private IQxTenantConfigService tenantConfigService;

    IHandleCommonPrePayService handleCommonPrePayService;
    @Autowired
    private WxPayBaseConfig payBaseConfig;


    @Override

    public Map<String, Object> addOrder4Unified(PayOrderDetailDto payOrderDetail) throws Exception {
        //ORDER_TYPE_MEETING_CZ
        String orderType = payOrderDetail.getOrderType();
        String serviceName = null;

        if (PayConstant.ORDER_TYPE_MEETING_CZ.getCode().equals(orderType)) {
            serviceName = "meetingPrePayService";
            handleCommonPrePayService = ZeroApplicationContextUtil.getBean(serviceName, HandleMeetingPrePayServiceImpl.class);
        } else {
            throw new BizException("未知的订单类型");
        }


        PayPreDataVo payPreData = handleCommonPrePayService.getPrePayData(payOrderDetail);
        String payType = payPreData.getPayType();
        BigDecimal payTotal = payPreData.getPayTotal();
        //订单支付金额为零，则不需要进行真实支付

        String goodsId = CommonUtils.generateRandomString();
        String paySn = payPreData.getPaySn();
        String goodsName = payPreData.getGoodsName();
        if (!CommonUtils.isNull(goodsName)) {
            goodsName = "会议室充值";
        }
        Map<String, Object> result = new HashMap<>();
        String payStatus = null;
        QxTenantConfig tenantConfig = tenantConfigService.queryByTenantId(SessionUtils.getTenantId());
        String noceStr = WXPayUtil.generateNonceStr();
        Boolean isZero = !CommonUtils.isNull(payTotal) && payTotal.compareTo(new BigDecimal("0")) == 0;
        if (isZero) {
            payStatus = PayConstant.PAY_STATUS_WAIT_PAY.getCode();
        } else if (payType.equals(PayConstant.PAY_TYPE_WX.getCode())) {
            String traderType = "JSAPI"; //小程序支付
            String openId = SessionUtils.getOpenId();
            FarmWXPayConfig config = new FarmWXPayConfig(tenantConfig.getWxAppId(), tenantConfig.getMchId(), tenantConfig.getApiKey(), tenantConfig.getCertContent(), payBaseConfig.getWxNotifyUrl());

            tenantConfig.setCertContent(null);

            Map<String, String> jsonResult = WxPayUtils.unifiedorder(config, goodsId, paySn, payTotal, goodsName, openId, traderType);
            payStatus = PayConstant.PAY_STATUS_WAIT_PAY.getCode();
            String prepayId = jsonResult.get("prepay_id");
            long second = System.currentTimeMillis() / 1000L;
            if (CommonUtils.isNull(prepayId)) {
                throw new BizException("下单失败，未获取到有效的prepay_id");
            }
            String seconds = String.valueOf(second);
            Map<String, String> returnResult = new HashMap<>();
            returnResult.put("appId", tenantConfig.getWxAppId());
            returnResult.put("timeStamp", seconds);
            returnResult.put("nonceStr", noceStr);
            returnResult.put("package", "prepay_id=" + prepayId);//默认sign=WXPay
            returnResult.put("signType", WXPayConstants.SignType.HMACSHA256.name());
            String paySign = WXPayUtil.generateSignature(returnResult, config.getKey(), WXPayConstants.SignType.HMACSHA256);
            returnResult.put("paySign", paySign);
            returnResult.put("prepay_id", prepayId);
            returnResult.put("partnerid", tenantConfig.getMchId());//微信商户账号*/
            result.put("wxPayInfo", returnResult);
        } else if (payType.equals(PayConstant.PAY_TYPE_TRANSFER.getCode())) {
            //银行转账
            payStatus = PayConstant.PAY_STATUS_WAIT_UPLOAD_PAY_PROOF.getCode();
        } else {
            throw new BizException("支付方式选择异常");
        }
        List<PayPreRecorderDto> preRecorders = payPreData.getPreRecorders();
        List<PayRecorderInfo> recorderInfos = new ArrayList<>();
        List<String> orderIds = new ArrayList<>();
        for (PayPreRecorderDto dto : preRecorders) {
            PayRecorderInfo recorderInfo = new PayRecorderInfo();
            BeanUtil.copyProperties(dto, recorderInfo);
            recorderInfo.setPayType(payType);
            recorderInfo.setId(CommonUtils.generateRandomString());
            recorderInfo.setOrderSn(dto.getOrderSn());
            recorderInfo.setPayStatus(payStatus);
            recorderInfo.setPaySn(paySn);
            recorderInfo.setPayDetail(ZeroJsonUtils.entityToJson(tenantConfig));
            recorderInfo.setTotalAmount(dto.getTotalAmount());
            recorderInfos.add(recorderInfo);
            orderIds.add(recorderInfo.getOrderId());
        }
        List<PayRecorderInfo> queryForCheck = payRecorderInfoService.queryByOrderIds(orderIds);
        if (!CommonUtils.isNull(queryForCheck)) {
            for (PayRecorderInfo info : queryForCheck) {
                LocalDateTime payTime = info.getPayTime().plusSeconds(10L);
                //10s
                LocalDateTime localDateTime = LocalDateTime.now();
                if (localDateTime.isBefore(payTime) && payOrderDetail.getPayType().equals(PayConstant.PAY_TYPE_WX.getCode())) {
                    throw new BizException("两次支付时间不超过10秒，请稍后支付");
                }

            }
        }
        boolean removeOld = payRecorderInfoService.removeByOrderIds(orderIds);
        boolean saveResult = payRecorderInfoService.saveBatch(recorderInfos);
        if (!saveResult) {
            throw new BizException("支付结果异常");
        }
        result.put("payRecorderInfo", recorderInfos);
        //已向微信创建支付单，单未知是否支付已完成
        //orderIds;
        handleCommonPrePayService.handleCreatePrePayResult(orderIds, payType);
        result.put("zero", isZero);
        return result;
    }


    @Override
    public Map<String, Object> updateIsPayFinish4QueryOrder(PayOrderDetailDto orderDetail) throws Exception {
        String orderType = orderDetail.getOrderType();
        String serviceName = null;
        if (PayConstant.ORDER_TYPE_MEETING_CZ.getCode().equals(orderType)) {
            serviceName = "meetingPrePayService";
            handleCommonPrePayService = ZeroApplicationContextUtil.getBean(serviceName, HandleMeetingPrePayServiceImpl.class);
        } else {
            throw new BizException("未知的订单类型");
        }
        Map<String, Object> checkPayResult = new HashMap<>();
        List<String> orderIds = orderDetail.getOrderIds();
        List<String> finishOrderIds = new ArrayList<>();
        List<PayRecorderInfo> recorderInfos = payRecorderInfoService.queryByOrderIds(orderIds);
        if (CommonUtils.isNull(recorderInfos)) {
            throw new BizException("未找到支付信息");
        }
        List<PayRecorderInfo> noFinishAndPayByWx = new ArrayList<>();
        List<PayRecorderInfo> finished = new ArrayList<>();
        List<PayRecorderInfo> noFinished = new ArrayList<>();
        List<PayRecorderInfo> newFinishRecorders = new ArrayList<>();
        for (PayRecorderInfo info : recorderInfos) {
            //支付完成
            if (PayConstant.PAY_STATUS_FINISH.getCode().equals(info.getPayStatus())) {
                finished.add(info);
                finishOrderIds.add(info.getOrderId());
            } else {
                noFinished.add(info);
                if (PayConstant.PAY_TYPE_WX.getCode().equals(info.getPayType())) {
                    noFinishAndPayByWx.add(info);
                }
            }
        }
        if (!CommonUtils.isNull(noFinishAndPayByWx)) {
            for (PayRecorderInfo info : noFinishAndPayByWx) {
                Boolean isZero = info.getTotalAmount().compareTo(new BigDecimal("0")) == 0;
                QxTenantConfig tenantConfig = tenantConfigService.queryByTenantId(SessionUtils.getTenantId());
                String payResult = "";
                if (!isZero) {
                    FarmWXPayConfig config = new FarmWXPayConfig(tenantConfig.getWxAppId(), tenantConfig.getMchId(), tenantConfig.getApiKey(), tenantConfig.getCertContent());
                    tenantConfig.setCertContent(null);
                    Map<String, String> result = WxPayUtils.payFinish(config, info.getPaySn());
                    logger.error(ZeroJsonUtils.mapToJson(result));
                    payResult = result.get("result_code");
                }
                List<PaySuccessDto> dtos = new ArrayList<>();
                //TODO 判断交易状态
                if (isZero || payResult.equals("SUCCESS")) {
                    PaySuccessDto dto = new PaySuccessDto();
                    dto.setOrderId(info.getOrderId());
                    dto.setPaySn(info.getPaySn());
                    dto.setPayType(info.getPayType());
                    dtos.add(dto);
                    finishOrderIds.add(info.getOrderId());
                    info.setPayStatus(PayConstant.PAY_STATUS_FINISH.getCode());
                    newFinishRecorders.add(info);
                    noFinished.remove(info);
                    finished.add(info);
                }
                if (!CommonUtils.isNull(dtos)) {
                    handleCommonPrePayService.handlePaySuccess(dtos);
                }
                if (!CommonUtils.isNull(newFinishRecorders)) {
                    this.payRecorderInfoService.updateBatchById(newFinishRecorders);

                }
            }
        }
        checkPayResult.put("finish", finished);
        checkPayResult.put("noFinish", noFinished);
        return checkPayResult;
    }

    @Override
    public Boolean closeOrders(PayOrderDetailDto orderDetail) throws Exception {
        String orderId = orderDetail.getOrderId();
//        AdoptOrderInfo adoptOrderInfo = adoptOrderInfoService.getById(orderId);
//        if (CommonUtils.isNull(adoptOrderInfo)) {
//            throw new BizException("订单已不存在");
//        }
//        String orderSno = adoptOrderInfo.getOrderSn();
//        FarmWXPayConfig config = new FarmWXPayConfig();
//        return WxPayUtils.closeOrders(config, orderSno);
        return null;
    }


    @Override
    public String update4WxCallBack(HttpServletRequest request) throws Exception {
        Map<String, String> result = new HashMap<>();
        InputStream inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        String resultXml = outSteam.toString("utf-8");
        Map<String, String> params = WXPayUtil.xmlToMap(resultXml);
        outSteam.close();
        inStream.close();
        String mchId = params.get("mch_id");
        QxTenantConfig config = tenantConfigService.queryByMchId(mchId);
        String apiKey = config.getApiKey();
        result = WxPayUtils.wxCallbackToMap(params, apiKey);
        if (WxPayConstant.SUCCESS.equals(result.get("return_code"))) {
            String paySn = params.get("out_trade_no");//查询订单号
            PayRecorderInfo payRecorderInfo = payRecorderInfoService.queryByPaySn(paySn);
            if (CommonUtils.isNull(payRecorderInfo)) {
                result.put("return_code", "FAIL");
                result.put("return_msg", "return_code不正确");
            } else {
                PayOrderDetailDto orderDetail = new PayOrderDetailDto();
                List<String> orderIds = new ArrayList<>();
                orderIds.add(payRecorderInfo.getOrderId());
                orderDetail.setOrderIds(orderIds);
                orderDetail.setOrderType(payRecorderInfo.getOrderType());
                orderDetail.setPayType(payRecorderInfo.getPayType());
                SessionUtils.makeWxSessionForPay(payRecorderInfo.getTenantId(), payRecorderInfo.getPayUserId(), payRecorderInfo.getPayUserName(), request);
                Map<String, Object> checkUpdateResult = this.updateIsPayFinish4QueryOrder(orderDetail);
                if (!CommonUtils.isNull(checkUpdateResult.get("noFinish"))) {
                    result.put("return_code", "FAIL");
                    result.put("return_msg", "未支付成功");
                }
                //否则是返回失败信息
            }
        }
        return WXPayUtil.mapToXml(result);
    }

    @Override
    public Map<String, String> doRefund(String orderNumber, BigDecimal refundAmount, BigDecimal totalFee) {
        String orderId = "";
//        AdoptOrderInfo adoptOrderInfo = adoptOrderInfoService.getById(orderId);
//        if (CommonUtils.isNull(adoptOrderInfo)) {
//            throw new BizException("订单已不存在");
//        }
////        String orderSno = adoptOrderInfo.getOrderSn();
//        String rebackOrderNumber = "";
//        FarmWXPayConfig config = new FarmWXPayConfig();
//        try {
//            return WxPayUtils.doRefund(config, orderSno, rebackOrderNumber, refundAmount, totalFee);
//        } catch (Exception e) {
//            e.printStackTrace();
//            throw new BizException(e.getMessage());
//        }
        return null;
    }

    @Override
    public Map<String, String> saveDoRefund(PayRefundOrderDto refundDto) {
        String refundOrderId = refundDto.getRefundOrderId();
        String orderType = refundDto.getOrderType();
        String serviceName = "";
        if (PayConstant.ORDER_TYPE_MEETING_CZ.getCode().equals(orderType)) {
            serviceName = "meetingPrePayService";
            handleCommonPrePayService = ZeroApplicationContextUtil.getBean(serviceName, HandleMeetingPrePayServiceImpl.class);
        } else {
            throw new BizException("未知的订单类型");
        }
        PayPreRefundVo vo = handleCommonPrePayService.getPreRefundData(refundOrderId, orderType, refundDto.getExtend());
        if (CommonUtils.isNull(vo)) {
            throw new BizException("退款异常");
        }
        String paySn = vo.getPaySn();
        String refundSn = OrderUtils.generateOrderSn();
        BigDecimal refundAmount = vo.getRefundAmount();
        BigDecimal totalFee = vo.getTotalFee();
        String payType = vo.getPayType();

        Boolean isZero = !CommonUtils.isNull(totalFee) && totalFee.compareTo(new BigDecimal("0")) == 0;

        if (PayConstant.ORDER_TYPE_MEETING_CZ.getCode().equals(orderType) && !CommonUtils.isNull(payType) && payType.equals(PayConstant.PAY_TYPE_TRANSFER.getCode())) {
            //进行转账退款处理操作
            boolean refundResult = handleCommonPrePayService.handleRefundSuccess(refundOrderId, orderType, refundDto.getExtend());
            if (!refundResult) {
                throw new BizException("退款异常，内部处理异常");
            }
            Map<String, String> result = new HashMap<>();
            result.put("result_code", "SUCCESS");
            return result;
        } else {
            QxTenantConfig tenantConfig = tenantConfigService.queryByTenantId(SessionUtils.getTenantId());
            FarmWXPayConfig config = new FarmWXPayConfig(tenantConfig.getWxAppId(), tenantConfig.getMchId(), tenantConfig.getApiKey(), tenantConfig.getCertContent());
            try {
                boolean refundResult = handleCommonPrePayService.handleRefundSuccess(refundOrderId, orderType, refundDto.getExtend());
                if (!refundResult) {
                    throw new BizException("退款异常，内部处理异常");
                }
                if (!isZero) {
                    Map<String, String> result = WxPayUtils.doRefund(config, paySn, refundSn, refundAmount, totalFee);
                    logger.error(ZeroJsonUtils.mapToJson(result));
                    return result;
                } else {
                    Map<String, String> result = new HashMap<>();
                    result.put("result_code", "SUCCESS");
                    return result;
                }

            } catch (Exception e) {
                e.printStackTrace();
                throw new BizException("退款异常");
            }
        }


    }

    @Override
    public Map<String, String> handleRefund(PayRefundOrderDto refundDto) {
        String refundOrderId = refundDto.getRefundOrderId();
        String orderType = refundDto.getOrderType();
        String serviceName = "";
        if (PayConstant.ORDER_TYPE_MEETING_CZ.getCode().equals(orderType)) {
            serviceName = "meetingPrePayService";
            handleCommonPrePayService = ZeroApplicationContextUtil.getBean(serviceName, HandleMeetingPrePayServiceImpl.class);
        } else {
            throw new BizException("未知的订单类型");
        }

        PayPreRefundVo vo = handleCommonPrePayService.getPreRefundData(refundOrderId, orderType, refundDto.getExtend());
        BigDecimal totalFee = vo.getTotalFee();
        String paySn = vo.getPaySn();
        String refundSn = OrderUtils.generateOrderSn();
        BigDecimal refundAmount = vo.getRefundAmount();
        String payType = vo.getPayType();

        Boolean isZero = !CommonUtils.isNull(totalFee) && totalFee.compareTo(new BigDecimal("0")) == 0;

        if (PayConstant.ORDER_TYPE_MEETING_CZ.getCode().equals(orderType) && !CommonUtils.isNull(payType) && payType.equals(PayConstant.PAY_TYPE_TRANSFER.getCode())) {
            //进行转账退款处理操作
            boolean refundResult = handleCommonPrePayService.handleRefundSuccess(refundOrderId, orderType, refundDto.getExtend());
            if (!refundResult) {
                throw new BizException("退款异常，内部处理异常");
            }
            Map<String, String> result = new HashMap<>();
            result.put("result_code", "SUCCESS");
            return result;
        } else {
            QxTenantConfig tenantConfig = tenantConfigService.queryByTenantId(SessionUtils.getTenantId());
            FarmWXPayConfig config = new FarmWXPayConfig(tenantConfig.getWxAppId(), tenantConfig.getMchId(), tenantConfig.getApiKey(), tenantConfig.getCertContent());
            try {
                boolean refundResult = handleCommonPrePayService.handleRefundSuccess(refundOrderId, orderType, refundDto.getExtend());
                if (!refundResult) {
                    throw new BizException("退款异常，内部处理异常");
                }
                if (!isZero) {
                    Map<String, String> result = WxPayUtils.doRefund(config, paySn, refundSn, refundAmount, totalFee);
                    logger.error(ZeroJsonUtils.mapToJson(result));
                    return result;
                } else {
                    Map<String, String> result = new HashMap<>();
                    result.put("result_code", "SUCCESS");
                    return result;
                }

            } catch (Exception e) {
                e.printStackTrace();
                throw new BizException("退款异常");
            }
        }


    }


}
